/*******************************************************************
 *
 * Copyright (C), 2021-2022, LS, All rights reserved.
 *
 * Author      : 
 * Create Time : 2021-09-17
 * Version     : v0.1
 * Description : 
 *******************************************************************/
 
/*----------------------------Head file----------------------------*/
#include "ls_ble_type.h"
#include "ls_ble_queue.h"
#include "ls_ble_custom.h"
#include "ls_ble_custom_api.h"
#include "ls_ble_custom_protocol.h"
#include "ls_ble_sdk_config.h"
#include "user_config.h"

/*----------------------------macro file---------------------------*/
#define CUSTOM_PROTO_LOG_EN		1

#if CUSTOM_PROTO_LOG_EN
#include "ls_ble_log.h"
#define TAG                             "custom_pro"
#define CUSTOM_PROTO_LOG(level, ...)	LS_BLE_LOG_RAW_INFO(__VA_ARGS__) //LS_BLE_LOG_PRINT(level, TAG, __VA_ARGS__)
#else
#define CUSTOM_PROTO_LOG(...)
#endif

/*----------------------------type define--------------------------*/
#pragma pack(1)
typedef struct {
	uint8_t frame_seq;
	uint8_t frame_sum;
	uint16_t rest_len;
	uint16_t payload_idx;
}txInfoTace_t;

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[CUSTOM_FIRST_FRAME_DATA_MAX_SIZE];
}first_frame_t;

typedef struct {
	uint8_t frame_seq;
	uint8_t payload[CUSTOM_REST_FRAME_DATA_MAX_SIZE];
}rest_frame_t;

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[CUSTOM_TX_PKT_PAYLOAD_MAX_SIZE];
}txPkt_t;

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[CUSTOM_FIRST_FRAME_DATA_MAX_SIZE];
}rxPkt_t;


typedef struct {
    uint8_t size;
    uint8_t buf[CUSTOM_MAX_DATA_LEN];
} ls_ble_gatt_send_data_t;

#pragma pack()

/*----------------------------var define---------------------------*/
static void custom_evt_cb(custom_evt_t *p_evt);
static custom_profile_cb_t custom_cb = {
    .evt_handler = custom_evt_cb,
};

#define CUS_SEND_DATA_QUEUE_SIZE            19
static ls_ble_queue_t send_queue;
static ls_ble_gatt_send_data_t send_buf[CUS_SEND_DATA_QUEUE_SIZE];
static ls_ble_gatt_send_data_t send_data;   //Need global var or it will indicate fail.
static bool custom_ind_busy = false;

/*-------------------------func declaration------------------------*/

/*-----------------------------------------------------------------*/

bool custom_send_queue_is_full(void)
{
    if(ls_ble_get_queue_used(&send_queue) >= CUS_SEND_DATA_QUEUE_SIZE)
        return true;
    else
        return false;
}

bool custom_send_queue_is_empty(void)
{
	if(ls_ble_get_queue_used(&send_queue) <= 0)
		return true;
	else
		return false;
}

static uint16_t checksum(const uint8_t* pData, uint8_t len)
{
	uint16_t chksum = 0;
	for(uint16_t i=0;i<len;i++) {
		chksum += pData[i];
	}
	return chksum;
}



static bool custom_first_frame_enqueue(txPkt_t* txPkt)
{
    if(true == custom_send_queue_is_full())
        return false;
    
    memset(&send_data, 0, sizeof(send_data));
    if(txPkt->payload_len > CUSTOM_FIRST_FRAME_DATA_MAX_SIZE) {
		send_data.size = CUSTOM_FIRST_FRAME_DATA_MAX_SIZE + CUSTOM_PACKET_HEAD_SIZE;
	}
	else {
		send_data.size = txPkt->payload_len + CUSTOM_PACKET_HEAD_SIZE;
	}
    
    memcpy(send_data.buf, (uint8_t*)txPkt, send_data.size);
    ls_ble_enqueue(&send_queue, &send_data);

	return true;
}


static bool custom_rest_frame_enqueue(txInfoTace_t* trace, txPkt_t* txPkt, uint8_t len)
{
	if(len > CUSTOM_REST_FRAME_DATA_MAX_SIZE) {
		return false;
	}
    
    if(true == custom_send_queue_is_full())
        return false;
    
    memset(&send_data, 0, sizeof(send_data));
    send_data.size = len + 1;
    rest_frame_t* tx = (rest_frame_t*)send_data.buf;
    tx->frame_seq = trace->frame_seq;
    memcpy(tx->payload, &txPkt->payload[trace->payload_idx], len);
    ls_ble_enqueue(&send_queue, &send_data);
    
    return true;
}


void custom_upload_send_queue_data(void)
{
    if(true == custom_ind_busy)
        return;
    
    memset(&send_data, 0, sizeof(send_data));
    if(LS_BLE_SUCCESS == ls_ble_dequeue(&send_queue, &send_data)) {
#if(DEBUG_INFO > 0)
        if(ls_ble_get_queue_used(&send_queue) > 0) {
            LS_BLE_LOG_RAW_INFO("queue_used: %d, %d\n", ls_ble_get_queue_used(&send_queue), custom_ind_busy);
        }
#endif
        if(LS_BLE_SUCCESS == custom_indicate_data(send_data.buf, send_data.size)) {
            custom_ind_busy = true;
        }

        /*LS_BLE_LOG_RAW_INFO("tx: ");
        for(int i=0;i<send_data.size;i++) {
            LS_BLE_LOG_RAW_INFO("%x, ", send_data.buf[i]);
        }
        LS_BLE_LOG_RAW_INFO("\n");*/
    }
}


bool custom_upload_msg(uint8_t cmd, uint8_t *payload, uint16_t payload_len)
{
    if(payload_len > CUSTOM_TX_PKT_PAYLOAD_MAX_SIZE)
		return false;

    if(true == custom_send_queue_is_full())
        return false;
    
    txPkt_t txPkt;
    txInfoTace_t txInfoTrace;

	memset(&txPkt, 0, sizeof(txPkt));
	memset(&txInfoTrace, 0, sizeof(txInfoTrace));
    
    txPkt.head = CUS_PRO_HEAD_B1;
	txPkt.pro_ver = CUS_PRO_VER_B2;
	txPkt.cmd = cmd;
	txPkt.payload_len = payload_len;	
	memcpy(txPkt.payload, payload, payload_len);
	txPkt.crc = checksum(&txPkt.cmd, txPkt.payload_len + 3);
	
	txInfoTrace.payload_idx = 0;
	txInfoTrace.rest_len = txPkt.payload_len;
	txInfoTrace.frame_sum = 1;
	if(txInfoTrace.rest_len > CUSTOM_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.frame_sum += (txInfoTrace.rest_len - CUSTOM_FIRST_FRAME_DATA_MAX_SIZE)/CUSTOM_REST_FRAME_DATA_MAX_SIZE + 1;
	}
	
    custom_first_frame_enqueue(&txPkt);
	if(txInfoTrace.rest_len > CUSTOM_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.payload_idx += CUSTOM_FIRST_FRAME_DATA_MAX_SIZE;
		txInfoTrace.rest_len -= CUSTOM_FIRST_FRAME_DATA_MAX_SIZE;
	}
	else {
		txInfoTrace.rest_len = 0;
	}
    
    
    while(txInfoTrace.rest_len > 0) {
        txInfoTrace.frame_seq++;
        if(txInfoTrace.frame_seq < txInfoTrace.frame_sum) {
            //CUSTOM_PROTO_LOG(LOG_LVL_INFO, "Rest: %d, %d, %d, %d.\n", txInfoTrace.frame_seq, txInfoTrace.frame_sum, txInfoTrace.rest_len, txInfoTrace.payload_idx);
            if(txInfoTrace.rest_len < CUSTOM_REST_FRAME_DATA_MAX_SIZE) {
                custom_rest_frame_enqueue(&txInfoTrace, &txPkt, txInfoTrace.rest_len);
                txInfoTrace.rest_len = 0;
            }
            else {
                custom_rest_frame_enqueue(&txInfoTrace, &txPkt, CUSTOM_REST_FRAME_DATA_MAX_SIZE);
                txInfoTrace.payload_idx += CUSTOM_REST_FRAME_DATA_MAX_SIZE;
                txInfoTrace.rest_len -= CUSTOM_REST_FRAME_DATA_MAX_SIZE;
            }
        }
    }

    custom_upload_send_queue_data();
    
	return true;
}


void custom_evt_cb(custom_evt_t *p_evt)
{
    ls_ble_cus_cb_evt_param_t evt_param;
    evt_param.len = 0;

    switch(p_evt->evt_type)
    {
        case CUSTOM_EVT_INDICATION_ENABLED: {
            evt_param.evt = LS_BLE_CUS_CB_EVT_IND_ENABLE;
            ls_ble_custom_callback(&evt_param);
        } break;
        
        case CUSTOM_EVT_INDICATION_DISABLED: {
            evt_param.evt = LS_BLE_CUS_CB_EVT_IND_DISABLE;
            ls_ble_custom_callback(&evt_param);
        } break;
        
        case CUSTOM_EVT_INDICATION_COMPLETE: {
            custom_ind_busy = false;
            custom_upload_send_queue_data();
        } break;
        
        case CUSTOM_EVT_RECEIVE_DATA: {
            if(p_evt->length > sizeof(rxPkt_t))
                return;

            rxPkt_t* rx = (rxPkt_t*)p_evt->p_data;
            if(CUS_PRO_HEAD_B1 != rx->head || CUS_PRO_VER_B2 != rx->pro_ver) {
                CUSTOM_PROTO_LOG(LOG_LVL_ERROR, "Err pro id: %x, %x.\n", rx->head, rx->pro_ver);
                return;
            }

            if(rx->crc != checksum(&rx->cmd, rx->payload_len + 3)) {
                CUSTOM_PROTO_LOG(LOG_LVL_ERROR, "Err chksum: %x, %x.\n", rx->crc, checksum(&rx->cmd, rx->payload_len + 3));
                return;
            }
            
            evt_param.evt = LS_BLE_CUS_CB_EVT_RECEIVE_DATA;
            evt_param.cmd = rx->cmd;
            evt_param.p_data = rx->payload;
            evt_param.len = rx->payload_len;
            ls_ble_custom_callback(&evt_param);
        } break;
        
        default:
            break;
    }
}


void custom_protocol_init(void)
{
    ls_ble_queue_init(&send_queue, (void*)send_buf, CUS_SEND_DATA_QUEUE_SIZE, sizeof(ls_ble_gatt_send_data_t));
    custom_service_init(&custom_cb);
}


void custom_protocol_send_queue_clear(void)
{
    ls_ble_queue_flush(&send_queue);
}


void custom_protocol_disconnect_handle(void)
{
    ls_ble_queue_flush(&send_queue);
    custom_ind_busy = false;
}
